#ifndef _AUX_YATE_LOG_H_
#define _AUX_YATE_LOG_H_

#ifdef _MSC_VER
#pragma warning ( disable : 4251 )
#endif 

#ifdef _DISABLE_YATE_SDP_LOG_

#define S_TRACE(message) do { \
}while(0)

#define S_DEBUG(message) do { \
}while(0)

#define S_INFO(message) do { \
}while(0)

#define S_WARN(message) do { \
}while(0)

#define S_ERROR(message) do { \
}while(0)

#define S_FATAL(message) do { \
}while(0)

#define SP_TRACE(message) do { \
}while(0)

#define SP_DEBUG(message) do { \
}while(0)

#define SP_INFO(message) do { \
}while(0)

#define SP_WARN(message) do { \
}while(0)

#define SP_ERROR(message) do { \
}while(0)

#define SP_FATAL(message) do { \
}while(0)

#define SF_TRACE(message) do { \
}while(0)

#define SF_DEBUG(message) do { \
}while(0)

#define SF_INFO(message) do { \
}while(0)

#define SF_WARN(message) do { \
}while(0)

#define SF_ERROR(message) do { \
}while(0)

#define SF_FATAL(message) do { \
}while(0)


#else

#include <string>
#include <iostream>
#include <time.h>
#include <cstdlib>
#include <cstring>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <stdio.h>
#include <stdlib.h>

#include <sstream>
#include <memory>



#ifndef DISABLE_YATE_FMT

#define FMT_HEADER_ONLY 1
#include "yfmt/format.h"
#include "yfmt/xchar.h"
#include "yfmt/printf.h"
#include "yfmt/chrono.h"

#endif

#ifdef _WIN32
	#ifdef LIBYATE_EXPORTS
	#define YATE_API __declspec(dllexport)
	#else
	#ifndef LIBYATE_STATIC
	#define YATE_API __declspec(dllimport)
	#endif
	#endif
#endif

#ifndef YATE_API
#define YATE_API
#endif

namespace spdlog {
	class logger;
}

//#define SPDLOG_LEVEL_TRACE 0
//#define SPDLOG_LEVEL_DEBUG 1
//#define SPDLOG_LEVEL_INFO 2
//#define SPDLOG_LEVEL_WARN 3
//#define SPDLOG_LEVEL_ERROR 4
//#define SPDLOG_LEVEL_CRITICAL 5
//#define SPDLOG_LEVEL_OFF 6

const int yate_log_level_trace = 0;
const int yate_log_level_debug = 1;
const int yate_log_level_info = 2;
const int yate_log_level_warn = 3;
const int yate_log_level_error = 4;
const int yate_log_level_fatal = 5;


namespace TelEngine {


	YATE_API int debugLevel();

#ifndef YateDebugLevel

#define YateDebugLevel

	enum DebugLevel {
		DebugFail = 0,
		DebugTest = 1,
		DebugCrit = 2,
		DebugGoOn = DebugCrit,
		DebugConf = 3,
		DebugStub = 4,
		DebugWarn = 5,
		DebugMild = 6,
		DebugNote = 7,
		DebugCall = 8,
		DebugInfo = 9,
		DebugAll = 10
	};
#endif

	struct YATE_API source_loc{
		source_loc() = default;
		source_loc(const char *filename_in, int line_in, const char *funcname_in)
			: filename{ filename_in }
			, line{ line_in }
			, funcname{ funcname_in }
		{}

		bool empty() const{
			return line == 0;
		}
		const char *filename{ nullptr };
		int line{ 0 };
		const char *funcname{ nullptr };
	};



	struct YATE_API log_stream 
	{
	public:
		log_stream(const source_loc& _loc, int _lvl, const std::string& _prefix)
			: loc(_loc)
			, lvl(_lvl){
			if(_prefix.length()>0){
				prefix = "["+_prefix + "] ";
			}
		}

		~log_stream(){
			flush();
		}

		void flush();

		template<class T>
		log_stream& operator<<(const T& msg){
			(_stream) << msg;
			return *this;
		}

	private:
		source_loc loc;
		std::ostringstream _stream;
		int lvl = yate_log_level_info;
		std::string prefix;
	};


	class YATE_API logger {

	public:

		static logger& get() {
			static logger logger;
			return logger;
		}

		void shutdown();

		template <typename... Args>
		void log(const source_loc& loc, int lvl, const char* fmt, const Args &... args);

		template <typename... Args>
		void printf(const source_loc& loc, int lvl, const char* fmt, const Args &... args) {
			logw(loc, lvl, (const char*)(fmt::sprintf(fmt, args...).c_str()));
		}

		template <typename... Args>
		void format(const source_loc& loc, int lvl, const char* fmt, const Args &... args) {
			logw(loc, lvl, (const char*)(fmt::format(fmt, args...).c_str()));
		}


		void logw(const source_loc& loc, int lvl, const char* fmt);

		int level() {
			return _log_level;
		}

		void set_level(int lvl);

		void set_flush_on(int lvl);

		bool is_trace_enable() {
			return _log_level <= yate_log_level_trace;
		}

		bool is_debug_enable() {
			return _log_level <= yate_log_level_debug;
		}


		std::shared_ptr<::spdlog::logger> loger_;

	private:
		logger(); // = default;
		~logger() = default;

		logger(const logger&) = delete;
		void operator=(const logger&) = delete;

	private:
		int _log_level;
	};

}



//#define f_debug(msg,...) TelEngine::logger::get().log({__FILE__, __LINE__, __FUNCTION__}, yate_log_level_debug, msg, ##__VA_ARGS__)
//#define f_info(msg,...)  TelEngine::logger::get().log({__FILE__, __LINE__, __FUNCTION__}, yate_log_level_info, msg, ##__VA_ARGS__)
//#define f_warn(msg,...)  TelEngine::logger::get().log({__FILE__, __LINE__, __FUNCTION__}, yate_log_level_warn, msg, ##__VA_ARGS__)
//#define f_error(msg,...) TelEngine::logger::get().log({__FILE__, __LINE__, __FUNCTION__}, yate_log_level_err, msg, ##__VA_ARGS__)
//#define f_fatal(msg,...) TelEngine::logger::get().log({__FILE__, __LINE__, __FUNCTION__}, yate_log_level_fatal, msg, ##__VA_ARGS__)


#define yate_s_stream_trace() TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_trace, "")
#define yate_s_stream_debug() TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_debug, "")
#define yate_s_stream_info()  TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_info, "")
#define yate_s_stream_warn()  TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_warn, "")
#define yate_s_stream_error() TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_error, "")
#define yate_s_stream_fatal() TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_fatal, "")

#define yate_h_stream_trace(a) TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_trace, a)
#define yate_h_stream_debug(a) TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_debug, a)
#define yate_h_stream_info(a)  TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_info, a)
#define yate_h_stream_warn(a)  TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_warn, a)
#define yate_h_stream_error(a) TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_error, a)
#define yate_h_stream_fatal(a) TelEngine::log_stream({__FILE__, __LINE__, __FUNCTION__},  yate_log_level_fatal, a)


// use like stream , e.g. S_INFO( "warn log: " << 1 );

#define LOG_S_TRACE(message) do { \
	if( TelEngine::debugLevel() > TelEngine::DebugAll ){		\
		yate_s_stream_trace() << message; \
	}		\
}while(0)

#define LOG_S_DEBUG(message) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugAll ){		\
		yate_s_stream_debug() << message; \
	}		\
}while(0)

#define LOG_S_INFO(message) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugInfo ){		\
		yate_s_stream_info() << message; \
	}		\
}while(0)

#define LOG_S_WARN(message) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugWarn ){		\
		yate_s_stream_warn() << message; \
	}		\
}while(0)

#define LOG_S_ERROR(message) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugCrit ){		\
		yate_s_stream_error() << message; \
	}		\
}while(0)

#define LOG_S_FATAL(message) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugFail ){		\
		yate_s_stream_fatal() << message; \
	}		\
}while(0)


#define LOG_S_TRACE_IF(condition,message) do { \
	if( (condition) && (TelEngine::debugLevel() > TelEngine::DebugAll) ){		\
		yate_s_stream_trace() << message; \
	}		\
}while(0)

#define LOG_S_DEBUG_IF(condition,message) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugAll ){		\
		yate_s_stream_debug() << message; \
	}		\
}while(0)

#define LOG_S_INFO_IF(condition,message) do { \
	if((condition) &&  TelEngine::debugLevel() >= TelEngine::DebugInfo ){		\
		yate_s_stream_info() << message; \
	}		\
}while(0)

#define LOG_S_WARN_IF(condition,message) do { \
	if((condition) &&  TelEngine::debugLevel() >= TelEngine::DebugWarn ){		\
		yate_s_stream_warn() << message; \
	}		\
}while(0)

#define LOG_S_ERROR_IF(condition,message) do { \
	if((condition) &&  TelEngine::debugLevel() >= TelEngine::DebugCrit ){		\
		yate_s_stream_error() << message; \
	}		\
}while(0)

#define LOG_S_FATAL_IF(condition,message) do { \
	if((condition) && TelEngine::debugLevel() >= TelEngine::DebugFail ){		\
		yate_s_stream_fatal() << message; \
	}		\
}while(0)




 // SP_XXX 
// use like sprintf, e.g. LOG_SP_INFO("warn log, %d-%d", 1, 2);

#define LOG_SP_TRACE(message,...) do { \
	if( TelEngine::debugLevel() > TelEngine::DebugAll ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_trace,message, ##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_DEBUG(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugAll ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_debug,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_INFO(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugInfo ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_info,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_WARN(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugWarn ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_warn,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_ERROR(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugCrit ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_error,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_FATAL(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugFail ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_fatal,message,##__VA_ARGS__); \
	}		\
}while(0)




#define LOG_SP_TRACE_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() > TelEngine::DebugAll ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_trace,message, ##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_DEBUG_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugAll ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_debug,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_INFO_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugInfo ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_info,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_WARN_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugWarn ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_warn,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_ERROR_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugCrit ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_error,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SP_FATAL_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugFail ){		\
		TelEngine::logger::get().printf({__FILE__, __LINE__, __FUNCTION__},yate_log_level_fatal,message,##__VA_ARGS__); \
	}		\
}while(0)


 // SF_XXX 
// use like format, e.g. LOG_SF_INFO("warn log, {}-{}", 1, 2);

#define LOG_SF_TRACE(message,...) do { \
	if( TelEngine::debugLevel() > TelEngine::DebugAll ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_trace,message, ##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_DEBUG(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugAll ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_debug,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_INFO(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugInfo ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_info,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_WARN(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugWarn ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_warn,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_ERROR(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugCrit ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_error,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_FATAL(message,...) do { \
	if( TelEngine::debugLevel() >= TelEngine::DebugFail ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_fatal,message,##__VA_ARGS__); \
	}		\
}while(0)


#define LOG_SF_TRACE_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() > TelEngine::DebugAll ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_trace,message, ##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_DEBUG_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugAll ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_debug,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_INFO_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugInfo ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_info,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_WARN_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugWarn ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_warn,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_ERROR_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugCrit ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_error,message,##__VA_ARGS__); \
	}		\
}while(0)

#define LOG_SF_FATAL_IF(condition,message,...) do { \
	if( (condition) && TelEngine::debugLevel() >= TelEngine::DebugFail ){		\
		TelEngine::logger::get().format({__FILE__, __LINE__, __FUNCTION__},yate_log_level_fatal,message,##__VA_ARGS__); \
	}		\
}while(0)


#define S_TRACE	   LOG_S_TRACE
#define S_DEBUG	   LOG_S_DEBUG
#define S_INFO	   LOG_S_INFO
#define S_WARN	   LOG_S_WARN
#define S_ERROR	   LOG_S_ERROR
#define S_FATAL	   LOG_S_FATAL


#endif // #ifndef _DISABLE_YATE_SDP_LOG_


#endif  // _AUX_YATE_LOG_H_